#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h> // 包含Adafruit_ST7735库
#include <DHT.h>
#include <NTPClient.h> // 包含NTPClient库
#include <ESP8266WiFi.h> // 包含WiFi库
#include <WiFiUdp.h> // 包含WiFiUdp库
#include <ESP8266HTTPClient.h> // 包含HTTPClient库
#include <ArduinoJson.h> // 包含ArduinoJson库

// DHT传感器配置
#define DHTPIN D4     // 连接DHT11传感器的引脚
#define DHTTYPE DHT11   // DHT 11
DHT dht(DHTPIN, DHTTYPE);

// TFT屏幕引脚定义
#define TFT_CS     D8
#define TFT_RST    D2
#define TFT_DC     D3
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

// NTP客户端配置
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 28800); // 东八区时间偏移

// WiFi信息
const char* ssid = "WiFi"; // 替换为你的WiFi名称
const char* password = "password"; // 替换为你的WiFi密码

// OpenWeatherMap API信息
const char* openWeatherMapApiKey = "API密钥"; // 替换为你的OpenWeatherMap API密钥
const char* city = "cityName"; // 替换为你想要查询的城市
const char* openWeatherMapServer = "http://api.openweathermap.org/data/2.5/weather?q=";

// 全局变量
unsigned long lastWeatherUpdate = 0;
const unsigned long weatherUpdateInterval = 10000; // 10秒

void setup() {
  Serial.begin(9600); // 初始化硬件串口，设置波特率为9600
  dht.begin();

  // 初始化SPI接口
  SPI.begin();

  // 初始化TFT屏幕
  tft.initR(INITR_BLACKTAB); // 初始化屏幕，使用黑色标签初始化
  tft.setRotation(0); // 设置屏幕旋转方向
  tft.fillScreen(ST7735_BLACK); // 清屏，设置背景颜色为黑色

  // 连接WiFi
  connectToWiFi();

  // 初始化NTP客户端
  timeClient.begin();
}

void connectToWiFi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to WiFi");
}

String getWeatherInfo() {
  if (WiFi.status() == WL_CONNECTED) {
    WiFiClient client;
    HTTPClient http;
    String serverPath = String(openWeatherMapServer) + String(city) + "&appid=" + String(openWeatherMapApiKey) + "&units=metric";

    // 发送HTTP请求
    http.begin(client, serverPath);
    int httpResponseCode = http.GET();

    if (httpResponseCode > 0) {
      String payload = http.getString();
      http.end();

      // 解析JSON数据
      DynamicJsonDocument doc(1024);
      DeserializationError error = deserializeJson(doc, payload);
      if (!error) {
        String weatherDescription = doc["weather"][0]["description"];
        float temperature = doc["main"]["temp"];
        return String(city) + ": " + weatherDescription + ", Temp: " + String(temperature) + " C";
      } else {
        return "";
      }
    } else {
      http.end();
      return "";
    }
  } else {
    return "";
  }
}

void loop() {
  unsigned long currentMillis = millis();

  // 更新天气信息
  if (currentMillis - lastWeatherUpdate >= weatherUpdateInterval) {
    updateWeatherInfo();
    lastWeatherUpdate = currentMillis;
  }

  // 获取当前时间和日期
  updateTimeAndDate();

  // 显示温度和湿度
  displayTemperatureAndHumidity();

  // 处理串口命令
  handleSerialCommands();
}

void updateWeatherInfo() {
  String weatherInfo = getWeatherInfo();
  int colonIndex = weatherInfo.indexOf(":");
  int commaIndex = weatherInfo.indexOf(",");
  String cityName = weatherInfo.substring(0, colonIndex);
  String weatherDescription = weatherInfo.substring(colonIndex + 2, commaIndex);
  String cityTemp = weatherInfo.substring(commaIndex + 8);

  // 在TFT屏幕上分别显示天气描述、温度和城市名称
  tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
  tft.setTextSize(1);
  tft.setCursor(0, 70);
  // tft.print("weather: ");
  tft.print(weatherDescription);
  tft.setCursor(0, 80);
  tft.print("citytemp: ");
  tft.print(cityTemp);
  tft.setCursor(0, 90);
  tft.print("cityname: ");
  tft.print(cityName);
}

void updateTimeAndDate() {
  timeClient.update();
  String currentTime = timeClient.getFormattedTime();

  // 在TFT屏幕上显示时间
  tft.setTextColor(ST7735_CYAN, ST7735_BLACK);
  tft.setTextSize(2);
  tft.setCursor(19, 25);
  tft.print(currentTime);

  // 获取当前日期
  time_t epochTime = timeClient.getEpochTime();
  struct tm *ptm = gmtime((time_t *)&epochTime);
  int year = ptm->tm_year + 1900;
  int month = ptm->tm_mon + 1;
  int day = ptm->tm_mday;
  int weekday = ptm->tm_wday; // 获取星期几，0表示星期日，1表示星期一，依此类推

  // 定义星期几的字符串数组
  const char* weekdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

  // 在TFT屏幕上显示日期和星期
  tft.setTextColor(ST7735_WHITE, ST7735_BLACK);
  tft.setTextSize(1);
  tft.setCursor(25, 50);
  tft.print(year);
  tft.print("-");
  tft.print(month);
  tft.print("-");
  tft.print(day);
  tft.setCursor(87, 50); // 调整星期的位置到最右
  tft.print(weekdays[weekday]); // 显示星期几
}

void displayTemperatureAndHumidity() {
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  // 在TFT屏幕上显示温度和湿度，设置字体颜色为白色
  tft.setTextColor(ST7735_MAGENTA, ST7735_BLACK);
  tft.setTextSize(1);
  tft.setCursor(0, 110);
  tft.print("temp: ");
  tft.print(t);
  tft.print(" C");
  tft.setCursor(0, 120);
  tft.print("humi: ");
  tft.print(h);
  tft.print(" %");
}

void handleSerialCommands() {
  if (Serial.available()) {
    uint8_t incomingByte = Serial.read();
    
    // 检查是否接收到0x11
    if (incomingByte == 0x11) {
      float t = dht.readTemperature();
      Serial.println(t); // 发送温度数值，保留一位小数
    }
    // 检查是否接收到0x22
    else if (incomingByte == 0x22) {
      float h = dht.readHumidity();
      Serial.println(h); // 发送湿度数值，保留一位小数
    }
    // 检查是否接收到0x33
    else if (incomingByte == 0x33) {
      timeClient.update();
      time_t epochTime = timeClient.getEpochTime();
      struct tm *ptm = gmtime((time_t *)&epochTime);
      int month = ptm->tm_mon + 1;
      Serial.println(month);
    }
    // 检查是否接收到0x44
    else if (incomingByte == 0x44) {
      timeClient.update();
      time_t epochTime = timeClient.getEpochTime();
      struct tm *ptm = gmtime((time_t *)&epochTime);
      int day = ptm->tm_mday;
      Serial.println(day);
    }
    // 检查是否接收到0x55
    else if (incomingByte == 0x55) {
      timeClient.update();
      time_t epochTime = timeClient.getEpochTime();
      struct tm *ptm = gmtime((time_t *)&epochTime);
      int weekday = ptm->tm_wday; // 获取星期几，0表示星期日，1表示星期一，依此类推

      // 发送星期几的整数值
      Serial.println(weekday);
    }
    // 检查是否接收到0x66
    else if (incomingByte == 0x66) {
      timeClient.update();
      String currentTime = timeClient.getFormattedTime();
      int hour = currentTime.substring(0, 2).toInt();
      Serial.println(hour);
    }
    // 检查是否接收到0x77
    else if (incomingByte == 0x77) {
      timeClient.update();
      String currentTime = timeClient.getFormattedTime();
      int minute = currentTime.substring(3, 5).toInt();
      Serial.println(minute);
    }
    // 检查是否接收到0x88
    else if (incomingByte == 0x88) {
      String weatherInfo = getWeatherInfo();
      int commaIndex = weatherInfo.indexOf(",");
      String cityTemp = weatherInfo.substring(commaIndex + 8);
      Serial.println(cityTemp);
    }
    // 检查是否接收到0x99
    else if (incomingByte == 0x99) {
      String weatherInfo = getWeatherInfo();
      int colonIndex = weatherInfo.indexOf(":");
      int commaIndex = weatherInfo.indexOf(",");
      String weatherDescription = weatherInfo.substring(colonIndex + 2, commaIndex);

      // 定义天气描述到数字的映射
      const char* weatherDescriptions[] = {
        "clear sky", "few clouds", "scattered clouds", "broken clouds", "overcast clouds",
        "light rain", "moderate rain", "heavy rain", "very heavy rain"
      };
      int weatherNumbers[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

      // 查找天气描述对应的数字
      int weatherNumber = -1;
      for (int i = 0; i < 9; i++) {
        if (weatherDescription.equalsIgnoreCase(weatherDescriptions[i])) {
          weatherNumber = weatherNumbers[i];
          break;
        }
      }

      // 发送天气对应的数字
      if (weatherNumber != -1) {
        Serial.println(weatherNumber);
      } else {
        Serial.println("Unknown weather description");
      }
    }
  }
}